package co.angluzha.commons.redis.aspect;

import co.angluzha.commons.redis.annotation.DistributedLock;
import co.angluzha.commons.redis.utils.RedisLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.Ordered;;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

/**
 * co.angluzha.commons.redis.aspect文档说明
 *
 * @author Angluzha
 * @version 2022/11/23
 */
@Aspect
@Component
@Slf4j
public class DistributedLockAop implements Ordered {
    /**
     * spEL表达式分析器
     */
    private ExpressionParser spelParser = new SpelExpressionParser();
    /**
     * 从方法中获取形参名称列表
     */
    private LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
    /**
     *  将切点方法实参加载到context中，再与注解中的Value值通过spEl表达式的方式组成锁名称
     *  try{
     *      尝试获取锁
     *      执行业务方法
     *      返回业务结果
     *  }finally{
     *      关闭锁
     *  }
     * @param joinPoint
     * @param distributedLock
     * @return
     * @throws Throwable
     */
    @Around("@annotation(distributedLock)")
    public Object DistributedLock(ProceedingJoinPoint joinPoint,DistributedLock distributedLock) throws Throwable {

        Object[] paramValue = joinPoint.getArgs();

        String[] paramNames =localVariableTableParameterNameDiscoverer
                                .getParameterNames(((MethodSignature) joinPoint.getSignature())
                                        .getMethod());

        EvaluationContext context = new StandardEvaluationContext();

        for (int i = 0; i < paramNames.length; i++) {
            context.setVariable(paramNames[i],paramValue[i]);
        }

        Expression expression = spelParser.parseExpression(distributedLock.value());

        String value = expression.getValue(context, String.class);

        log.info("[Lock] 🔒名称为---> {}",value);

        try {
            RedisLock.lock(value);
            Object proceed = joinPoint.proceed();
            return proceed;
        }finally {
            RedisLock.unlock();
        }
    }

    @Override
    public int getOrder() {
        return -1;
    }
}
